P3628 [APIO2010]特别行动队

闲扯

刚学斜率 $DP$ ,做了两道例题,感觉有点感觉了,还不错。

这是我第一道没看题解自己做出来的题,写篇题解记录一下。

题面

P3628 [APIO2010]特别行动队

Solution

先考虑朴素 $DP$ 。

我们可以比较简单的推出以下方程:

  • $ dp_i=\min_{j=0}^{i-1}(dp_j+a(sum_i-sum_j)^2+b(sum_i-sum_j)+c)$

如果直接枚举,复杂度是 $\frac{n^2}{2}$ 的,对于本题一定会 $TLE$ 。

考虑怎么优化。

先将原式中的括号全部打开,得到下面的式子:

  • $ dp_i=\min_{j=0}^{i-1}(dp_j+a\cdot sum_i^2-2a\cdot sum_j\cdot sum_i+a\cdot sum_j^2+b\cdot sum_i-b\cdot sum_j+c)$

可以发现式中存在有同时与 $i$ 和 $j$ 有关的项,可以使用斜率优化。

先将 $\min$ 去掉,并进行移项处理,将原式化为如下形式:

  • $a\cdot sum_j^2-b\cdot sum_j+dp_j=2a\cdot sum_i\cdot sum_j-a\cdot sum_i^2-b\cdot sum_i-c+dp_i$

可以看作是一条斜率为 $2a*sum_i$ 的直线,而我们要做的是让纵截距最大。

因为斜率递减( $a<0$ ),而且维护最大值,考虑维护一个上凸壳。

每次将斜率小于当前斜率的出队,每次用队首元素更新。

入队时,保证上凸壳的斜率是单调递减的即可。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include<bits/stdc++.h>
#define del(a,i) memset(a,i,sizeof(a))
#define ll long long
#define inl inline
#define il inl void
#define it inl int
#define ill inl ll
#define re register
#define ri re int
#define rl re ll
#define mid ((l+r)>>1)
#define lowbit(x) (x&(-x))
#define INF 0x3f3f3f3f
using namespace std;
template<class T>il read(T &x){
int f=1;char k=getchar();x=0;
for(;k>'9'||k<'0';k=getchar()) if(k=='-') f=-1;
for(;k>='0'&&k<='9';k=getchar()) x=(x<<3)+(x<<1)+k-'0';
x*=f;
}
template<class T>il print(T x){
if(x/10) print(x/10);
putchar(x%10+'0');
}
ll mul(ll a,ll b,ll mod){long double c=1.;return (a*b-(ll)(c*a*b/mod)*mod)%mod;}
it qpow(int x,int m,int mod){
int res=1,bas=x%mod;
while(m){
if(m&1) res=(1ll*res*bas)%mod;
bas=(1ll*bas*bas)%mod,m>>=1;
}
return res%mod;
}
const int MAXN = 1e6+5;
int n,a,b,c,q[MAXN],l=1,r=1;
ll sum[MAXN],dp[MAXN];
#define x(i) (sum[i])
#define y(i) (a*sum[i]*sum[i]-b*sum[i]+dp[i])
inl double slope(int x,int y){return 1.*(y(x)-y(y))/(x(x)-x(y));}
int main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
read(n),read(a),read(b),read(c);
for(ri i=1;i<=n;++i) read(sum[i]),sum[i]+=sum[i-1];
for(ri i=1;i<=n;++i){
while(l<r&&slope(q[l],q[l+1])>=2*a*sum[i]) ++l;
dp[i]=dp[q[l]]+a*(sum[i]-sum[q[l]])*(sum[i]-sum[q[l]])+b*(sum[i]-sum[q[l]])+c;
while(l<r&&slope(q[r-1],q[r])<=slope(q[r-1],i)) --r;
q[++r]=i;
}
print(dp[n]);
return 0;
}

总结

今天写到一半,结果老师突然来考试。。。

最后写的有点水,以后想起来再更吧。。